#pragma once

#include <matsu.h>
#include "SRType.h"

namespace sora
{
	class Model;
	class Camera;
	class Transformation;
	class SceneNode;
	class SceneNodeGroup;
	class ModelNode;
	class SceneNodeVisitor;
	class Light;
	class LightNode;

	//camera 조작에 도움이 되느 helper function 모음 수준. static으로 안한건 데이터로써 쉐이더에 인자로 접근할수 있는 가능성을 남기고 싶어서이다
	class Camera {
	public:
		void setOrtho(float left, float right, float bottom, float top, float near, float far);
		void setOrthoMatrix(const matsu::mat4 &mat);
		void setPerspective(float fovy, float aspect, float zNear, float zFar);
		void setFrustum(float left, float right, float bottom, float top, float near, float far);
		void lookAt(const matsu::vec3 &eye, const matsu::vec3 &target, const matsu::vec3 &up);
		const float *pointer() const;		
	private:
		matsu::mat4 matrix_;
	};

	class Transformation {
	public:
		Transformation(TransformationType type, float x, float y, float z);
		~Transformation();

		//생성시에 type넣는건 귀찮으니까 static으로 더 쉽게 생성할수 있게 하자
		static Transformation translate(float x, float y, float z);
		static Transformation scale(float x, float y, float z);
		static Transformation scale(float s);
		static Transformation rotate(float xrot, float yrot, float zrot);
		static Transformation rotateX(float xrot);
		static Transformation rotateY(float yrot);
		static Transformation rotateZ(float zrot);

		TransformationType type() const;
		
		float scaleX() const;
		float scaleY() const;
		float scaleZ() const;

		float translateX() const;
		float translateY() const;
		float translateZ() const;

		float rotateX() const;
		float rotateY() const;
		float rotateZ() const;
		
	private:
		//union을 써서 동시에 표현
		TransformationType type_;
		union {
			struct {
				float scaleX_;
				float scaleY_;
				float scaleZ_;
			};
			struct {
				float translateX_;
				float translateY_;
				float translateZ_;
			};
			struct {
				float rotateX_;
				float rotateY_;
				float rotateZ_;
			};
		};
	};

	class SceneNode {
	public:
		SceneNode();
		virtual ~SceneNode();

		virtual void accept(SceneNodeVisitor &visitor) = 0;
	private:
	};
	class SceneNodeGroup : public SceneNode {
	public:
		SceneNodeGroup();
		virtual ~SceneNodeGroup();

		//transformation
		void transformation(const Transformation &t);
		std::vector<Transformation*>::const_iterator transformationListBegin() const;
		std::vector<Transformation*>::const_iterator transformationListEnd() const;

		//children
		void addChild(SceneNode* child);
		SceneNode *child(int index) const;
		int numChild() const;

		//light
		void addLight(LightNode *light);
		LightNode *light(int index) const;
		int numLight() const;

		void accept(SceneNodeVisitor &visitor);

	private:
		std::vector<SceneNode*> children_;
		std::vector<Transformation*> transformationList_;
		std::vector<LightNode*> lightList_;
	};

	class ModelNode : public SceneNode {
	public:
		ModelNode(ModelPtr model);
		virtual ~ModelNode();
		Model *model() const;
		void accept(SceneNodeVisitor &visitor);

	private:
		ModelPtr model_;
	};

	class LightNode : public SceneNode {
	public:
		LightNode(const Light &light);
		virtual ~LightNode();
		const Light& light() const;
		void accept(SceneNodeVisitor &visitor);
	private:
		std::auto_ptr<Light> light_;
	};
}
